package cn.turboinfo.dongying.api.gateway.web.config

import cn.turboinfo.dongying.api.domain.common.usecase.AbstractUseCase
import mu.KotlinLogging
import nxcloud.ext.springmvc.automapping.contract.AutoMappingContractData
import nxcloud.ext.springmvc.automapping.spi.AutoMappingContractDataConverter
import nxcloud.ext.springmvc.automapping.spi.AutoMappingRequestParameterTypeResolver
import org.apache.commons.lang3.StringUtils
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.util.ClassUtils
import java.lang.reflect.Method

@Configuration
class CommonAutoMappingConfig {

    private val logger = KotlinLogging.logger {}

    /**
     * 将针对 UseCase 的自动映射处理方法名统一解析到 execute() 方法上
     */
    @Bean
    protected fun abstractUseCaseAutoMappingContractDataConverter(): AutoMappingContractDataConverter {
        return object : AutoMappingContractDataConverter {
            override fun convert(data: AutoMappingContractData): AutoMappingContractData {
                return data.copy(
                    beanMethod = "execute"
                )
            }

            override fun isSupported(data: AutoMappingContractData): Boolean {
                return AbstractUseCase::class.java.isAssignableFrom(data.beanType)
            }
        }
    }

    /**
     * 将 UseCase 的抽象入参类型解析为具体的实现类的入参类型
     */
    @Bean
    protected fun abstractUseCaseAutoMappingRequestParameterTypeResolver(): AutoMappingRequestParameterTypeResolver {
        return object : AutoMappingRequestParameterTypeResolver {
            override fun isSupported(method: Method): Boolean {
                return AbstractUseCase::class.java.isAssignableFrom(method.declaringClass)
            }

            override fun resolveParameterType(method: Method): Array<Class<*>> {
                // 需要去掉 Spring 生成的 AOP 代理类名字后缀
                val useCaseClassName =
                    StringUtils.substringBefore(method.declaringClass.canonicalName, ClassUtils.CGLIB_CLASS_SEPARATOR)
                // 固定只有一个内部类参数
                return arrayOf(Class.forName("${useCaseClassName}\$InputData"))
            }
        }
    }
}
